<template>
  <div class="wrap">
    <div id="map"></div>
  </div>
</template>

<script>
import * as THREE from "three";
import * as maptalks from "maptalks";
import { ThreeLayer } from "maptalks.three";
import hanyujinguB from "./hanyujinguB.json";
import * as dat from "dat.gui";
import { lineLength } from "@/utils/geoUtils";
export default {
  name: "MTBuilding",
  data() {
    return {
      meshs: null,
      features: null,
      material: null,
      topColor: null,
      threeLayer: null,
    };
  },
  methods: {
    init() {
      // const map = new maptalks.Map('map', { /* options */ });
      // 地图底图
      const map = new maptalks.Map("map", {
        center: [117.13233, 36.6604537],
        zoom: 16,
        pitch: 67,
        // bearing: 180,

        // centerCross: true,
        doubleClickZoom: false,
        baseLayer: new maptalks.TileLayer("tile", {
          urlTemplate:
            "https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png",
          // urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
          subdomains: ["a", "b", "c", "d"],
          // urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
          // subdomains: ['a', 'b', 'c'],
          // cssFilter: 'sepia(10%) invert(80%)',
          attribution:
            '&copy; <a href="http://osm.org">OpenStreetMap</a> contributors, &copy; <a href="https://carto.com/">CARTO</a>',
        }),
      });

      // features to draw
      this.features = [];
      // hanyujinguB.forEach((b) => {
      //   this.features = this.features.concat(b.features);
      // });
      this.features = hanyujinguB.features;
      // the ThreeLayer to draw buildings   // three 图层
      this.threeLayer = new ThreeLayer("t", {
        forceRenderOnMoving: true,
        forceRenderOnRotating: true,
        // animation: true
      });

      this.meshs = [];
      this.material = this.getBuildingsMaterial();
      this.material.vertexColors = THREE.VertexColors;
      this.topColor = new THREE.Color("rgb(200, 200, 250)");

      (this.threeLayer.prepareToDraw = (gl, scene, camera) => {
        const light = new THREE.DirectionalLight(0xffffff);
        light.position.set(0, -10, 10).normalize();
        scene.add(light);
        camera.add(new THREE.PointLight("#fff", 2));

        const heightPerLevel = 8;
        const polygons = this.features.map((f) => {
          const polygon = maptalks.GeoJSON.toGeometry(f);
          const levels = f.properties["building:levels"] || 5;
          polygon.setProperties({
            height: heightPerLevel * levels,
          });
          return polygon;
        });

        const mesh = this.threeLayer.toExtrudePolygons(
          polygons,
          { interactive: false },
          this.material
        );

        const bufferGeometry = mesh.getObject3d().geometry;
        const geometry = new THREE.Geometry().fromBufferGeometry(
          bufferGeometry
        );

        const { vertices, faces, faceVertexUvs } = geometry;
        for (let i = 0, len = faces.length; i < len; i++) {
          const { a, b, c } = faces[i];
          const p1 = vertices[a],
            p2 = vertices[b],
            p3 = vertices[c];
          //top face
          if (p1.z > 0 && p2.z > 0 && p3.z > 0) {
            const vertexColors = faces[i].vertexColors;
            for (let j = 0, len1 = vertexColors.length; j < len1; j++) {
              vertexColors[j].r = this.topColor.r;
              vertexColors[j].g = this.topColor.g;
              vertexColors[j].b = this.topColor.b;
            }
            const uvs = faceVertexUvs[0][i];
            for (let j = 0, len1 = uvs.length; j < len1; j++) {
              uvs[j].x = 0;
              uvs[j].y = 0;
            }
          }
        }
        mesh.getObject3d().geometry = new THREE.BufferGeometry().fromGeometry(
          geometry
        );
        bufferGeometry.dispose();
        geometry.dispose();
        // const position = geometry.attributes.position.array;
        // const uv = geometry.attributes.uv.array;
        // const index = geometry.index.array;
        // for (let i = 0, len = index.length; i < len; i += 3) {
        //     const a = index[i], b = index[i + 1], c = index[i + 2];
        //     const z1 = position[a * 3 + 2], z2 = position[b * 3 + 2], z3 = position[c * 3 + 2];
        //     // top vertex
        //     if (z1 > 0 && z2 > 0 && z3 > 0) {
        //         uv[a * 2] = 0;
        //         uv[a * 2 + 1] =  0;
        //         uv[b * 2] =  0;
        //         uv[b * 2 + 2] =  0;
        //         uv[c * 2] =  0;
        //         uv[c * 2 + 1] =  0;
        //     }
        // }
        console.log(mesh, 'mesh')
        this.meshs.push(mesh);
        this.threeLayer.addMesh(this.meshs);
        // 画路网
        this.addLines();
        // 画 point-icon
        this.addPoints();
        // 调用gui插件
        // this.initGui();
        this.animation();
      }),
        this.threeLayer.addTo(map);
    },
    // 获取建筑物材质
    // getBuildingsMaterial(color = 'red') {
    getBuildingsMaterial() {
      const texture = new THREE.TextureLoader().load(
        "static/textures/building2.png"
      );
      texture.needsUpdate = true; //使用贴图时进行更新
      texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
      // texture.repeat.set(0.002, 0.002);
      texture.repeat.set(1, 4);
      const material = new THREE.MeshPhongMaterial({
        map: texture,
        transparent: true,
        color: "#fff",
      });
      return material;
    },
    animation() {
      // layer animation support Skipping frames
      this.threeLayer._needsUpdate = !this.threeLayer._needsUpdate;
      if (this.threeLayer._needsUpdate) {
        this.threeLayer.renderScene();
      }
      requestAnimationFrame(this.animation);
    },
    // 根据geojson路网数据，绘制路网
    addLines() {
      let material = new THREE.MeshBasicMaterial({
        color: 0xd3887,
        transparent: true,
      });
      let highlightmaterial = new THREE.MeshBasicMaterial({
        color: 0x00ffff,
        transparent: true,
      });
      let lines, lineTrails;
      fetch("./static/geoJson/hanyuRoad.json")
        .then((res) => {
          return res.json();
        })
        .then((geojson) => {
          // geojson = LZString.decompressFromBase64(geojson);
          // geojson = JSON.parse(geojson);
          let lineStrings = maptalks.GeoJSON.toGeometry(geojson);
          let timer = "generate line time";
          console.time(timer);
          const list = [];
          // console.log(lineStrings, 'lineStrings')
          lineStrings.forEach((lineString) => {
            list.push({
              lineString,
              len: lineLength(lineString),
            });
          });
          // console.log(list, 'list')
          lineStrings = list.sort((a, b) => {
            return b.len - a.len;
          });

          lines = lineStrings.slice(0, 500).map((d) => {
            let line = this.threeLayer.toExtrudeLine(
              d.lineString,
              { altitude: 0, width: 3, height: 1 },
              material
            );
            return line;
          });
          lineTrails = lineStrings.slice(0, 100).map((d) => {
            let line = this.threeLayer.toExtrudeLineTrail(
              d.lineString,
              {
                altitude: 0,
                width: 3,
                height: 2,
                chunkLength: d.len / 40,
                speed: 0.6,
                trail: 2,
              },
              highlightmaterial
            );
            return line;
          });

          console.log("lines.length:", lines.length);
          console.timeEnd(timer);
          this.threeLayer.addMesh(lines);
          this.threeLayer.addMesh(lineTrails);
        });
    },
    // 根据经纬度坐标绘制icon-point
    addPoints() {
      let points = []
      const lnglats = [
        [117.1308597, 36.6670912],
        [117.1274703, 36.6581863],
        [117.1190835, 36.642772],
        [117.1206177, 36.6671084],
        [117.132176,  36.6715574]
      ];
      points = lnglats.map((lnglat) => {
        // 点的材质--不高亮
        const material = this.createMateria();
        // 点的材质--高亮
        const highlightmaterial = this.createMateria('red');
        const point = this.threeLayer.toPoint(
          lnglat,
          // { height: 300 * Math.random() },
          { height: 100 },
          material
        );
        // tooltip test
        point.setToolTip(point.getId(), {
          showTimeout: 0,
          eventsPropagation: true,
          dx: 10,
        });

        //infowindow test
        point.setInfoWindow({
          content: "id:" + point.getId(),
          title: "message",
          animationDuration: 500,
          autoOpenOn: false,
        });
        //event test
        [
          "click",
          "mouseout",
          "mouseover",
          "mousedown",
          "mouseup",
          "dblclick",
          "contextmenu",
        ].forEach( eventType => {
          point.on(eventType,  function(e) {
            // console.log(e.type, e);
            // console.log(this, '看看这个this');
            if (e.type === "mouseout") {
              this.setSymbol(material);
            }
            if (e.type === "mouseover") {
              this.setSymbol(highlightmaterial);
            }
          });
        });
        return point;
      });
      // console.log(points, '看看points')
      this.threeLayer.addMesh(points);
    },
    // piont-icon材质
    createMateria(fillStyle) {
            // const idx = Math.floor(Math.random() * 3);
            return new THREE.PointsMaterial({
                // size: 10,
                sizeAttenuation: false,
                color: fillStyle,
                // alphaTest: 0.5,
                // vertexColors: THREE.VertexColors,
                //  transparent: true
                // color: 0xffffff,
                size: 40,
                transparent: true, //使材质透明
                blending: THREE.AdditiveBlending,
                depthTest: true, //深度测试关闭，不消去场景的不可见面
                depthWrite: false,
                map: new THREE.TextureLoader().load(`static/textures/2.svg`)
                //刚刚创建的粒子贴图就在这里用上
            });
        },
    // gui插件配置
    initGui() {
      const params = {
        add: true,
        color: this.material.color.getStyle(),
        show: true,
        opacity: 1,
        altitude: 0,
        animateShow: this.animateShow,
      };
      const gui = new dat.GUI();
      gui.add(params, "add").onChange(() => {
        if (params.add) {
          this.threeLayer.addMesh(this.meshs);
        } else {
          this.threeLayer.removeMesh(this.meshs);
        }
      });
      gui
        .addColor(params, "color")
        .name("building color")
        .onChange(() => {
          this.material.color.set(params.color);
          this.meshs.forEach((mesh) => {
            mesh.setSymbol(this.material);
          });
        });
      gui.add(params, "opacity", 0, 1).onChange(() => {
        this.material.opacity = params.opacity;
        this.meshs.forEach((mesh) => {
          mesh.setSymbol(this.material);
        });
      });
      gui.add(params, "altitude", 0, 300).onChange(() => {
        this.meshs.forEach((mesh) => {
          mesh.setAltitude(params.altitude);
        });
      });
      gui.add(params, "animateShow");
    },
    animateShow() {
      this.meshs.forEach((mesh) => {
        mesh.animateShow({
          duration: 3000,
        });
      });
    },
  },
  mounted() {
    this.init();
  },
};
</script>

<style scoped>
#map {
  width: 100%;
  height: 100vh;
  position: relative;
  overflow: hidden;
  background-color: #000;
}
</style>